// HxGo Web Application
package app

import (
	"os"
	"path"
	"github.com/fuxiaohei/hxgo"
	"github.com/Unknwon/com"
	"net/http"
	"time"
	"fmt"
	"runtime/debug"
)

// HxGo Application version
const (
	HxGo_AppVersion = "0.2.8"
)

// global vars
var (
	// app name
	Name string
	// app version
	Version string
	// app root path, set as relative uri, get absolute uri after Run called
	Root string
	// logger struct
	Log *hxgo.Logger
	// init function, call it after all default methods
	initFunc func ()
	// abort function, call it if panic run or app abort method
	abortFunc func (msg string)
	// after init func, call it before http start
	afterInit func()
)

// package init method
func init() {
	Name = "HxGo"
	Version = HxGo_AppVersion
	Root = "sample"
	Log = hxgo.NewLogger()
	abortFunc = func(msg string) {
		Log.Panic(msg)
		Log.Panic(string(debug.Stack()))
		os.Exit(-1)
	}
}

// create folders if Root directory is empty
func initFolder() {
	d, _ := os.Getwd()
	Root = path.Join(d, Root)
	if !com.IsDir(Root) {
		os.Mkdir(Root, os.ModeDir)
		// create sub-directories
		folders := []string{"utils", "views", "public","upload"}
		for _, p := range folders {
			p = path.Join(Root, p)
			os.Mkdir(p, os.ModeDir)
		}
		Log.Info("[" + Name + "] create app folder")
	}
}

// Set init function
// init function will be called after all default init methods and before handler initialization and http server starting
func InitFunc(fn func ()) {
	initFunc = fn
}

// Set abort function
// abort function will be called in panic recover function and app.Abort method
func AbortFunc(fn func (msg string)) {
	abortFunc = fn
}

// Set after-init function
// after init function will be called after all global vars inited, before http server start
func AfterInit(fn func()){
	afterInit = fn
}

// Run web application
func Run() {
	// init folder if not created
	initFolder()
	// init http handlers
	if Handler == nil {
		initHandler()
	}
	// do custom init
	if initFunc != nil {
		initFunc()
	}
	// init config data
	if Cfg == nil {
		initConfig()
	}
	// init view struct
	if View == nil {
		initView()
	}
	// init database connection
	if Db == nil {
		initDb()
	}
	// set defer func if panic
	defer func() {
		e := recover()
		if e != nil {
			abortFunc(fmt.Sprint(e))
		}
	}()

	if afterInit != nil{
		afterInit()
	}

	// create http server
	s := http.Server{
		Addr:Cfg.StringOr("HttpHost", "localhost") + ":" + Cfg.StringOr("HttpPort", "8080"),
		Handler:Handler,
		ReadTimeout:    time.Duration(Cfg.IntOr("HttpTimeout", 30))*time.Second,
		WriteTimeout:   time.Duration(Cfg.IntOr("HttpTimeout", 30))*time.Second,
		MaxHeaderBytes: 1<<20,
	}
	Log.Info("[" + Name + "] http " + s.Addr, "[" + RunMode + "]")

	// listen server
	e := s.ListenAndServe()
	if e != nil {
		panic(e)
	}
}

// app Abort
// it will kill main process
func Abort(msg string) {
	abortFunc(msg)
}

